// Implementation of the Socket class.
#include "Socket.h"
#include <iostream>
#include <cstring>
#include <errno.h>
#include <fcntl.h>

Socket::Socket() :
    m_sock ( -1 )
{
    memset ( &m_addr,
             0,
             sizeof ( m_addr ) );
}

Socket::~Socket()
{
    if ( is_valid() )
        ::close ( m_sock );
}

bool Socket::create()
{
    m_sock = socket ( AF_INET,
                      SOCK_STREAM,
                      0 );
    if ( ! is_valid() )
        return false;
    // TIME_WAIT - argh
    int on = 1;
    // enables local address reuse,
    if ( setsockopt ( m_sock, SOL_SOCKET, SO_REUSEADDR, ( const char* ) &on, sizeof ( on ) ) == -1 )
        return false;
    return true;
}

bool Socket::bind ( const int port )
{
    if ( ! is_valid() )
        return false;
    m_addr.sin_family = AF_INET;
    m_addr.sin_addr.s_addr = INADDR_ANY;
    m_addr.sin_port = htons ( port );
    int bind_return = ::bind ( m_sock,
                               ( struct sockaddr * ) &m_addr,
                               sizeof ( m_addr ) );
    if ( bind_return == -1 )
        return false;
    return true;
}

bool Socket::listen() const
{
    if ( ! is_valid() )
        return false;
    int listen_return = ::listen ( m_sock, MAXCONNECTIONS );
    if ( listen_return == -1 )
        return false;
    return true;
}

bool Socket::accept ( Socket& com_socket ) const
{
    int addr_length = sizeof ( m_addr );
    // NOTIC:reuse m_addr
    com_socket.m_sock = ::accept ( m_sock, ( sockaddr * ) &m_addr, ( socklen_t * ) &addr_length );
    if ( com_socket.m_sock <= 0 )
    {
        return false;
    }
    else
    {
        std::cout<<"accept:"<<inet_ntoa(m_addr.sin_addr)<<std::endl;
        return true;
    }
}

bool Socket::send( const std::string s ) const
{
    return Socket::send(s.c_str(), s.size());
}

bool Socket::send ( const char* c_psz, int nLen ) const
{
    int nPos = 0;
    while(nPos < nLen)
    {
        int status = ::send ( m_sock, &c_psz[nPos], nLen - nPos, MSG_NOSIGNAL );
        if ( status == -1 )
            return false;
        nPos += status;
    }
    return true;
}

int Socket::recv( std::string& s ) const
{
    int nPos = 0;
    char buf [ MAXRECV + 1 ] = {0};
    while (true)// (nPos < nLen)
    {
        memset(buf, 0, MAXRECV * sizeof(char));
        int status = ::recv ( m_sock, buf, MAXRECV, 0 );
        //std::cout<<"receive:"<<buf<<std::endl;
        
        if ( status == -1 )
        {
            std::cout << "status == -1   errno == " << errno << "  in Socket::recv\n";
            return 0;
        }
        else if ( status == 0 )
            return 0;
        s += buf;
        nPos += status;
        if (buf[status] == 0)   // not status-1
            break;
    }
    return s.size();
}

int Socket::recv (  char* pszBuf, int nMaxLem ) const
{
    int nPos = 0;
    char buf [ MAXRECV + 1 ] = {0};
    pszBuf[0] = 0;
    while (true)//(nPos < nLen)
    {
        memset(buf, 0, MAXRECV * sizeof(char));
        int status = ::recv ( m_sock, buf, MAXRECV, 0 );

        if ( status == -1 )
        {
            std::cout << "status == -1   errno == " << errno << "  in Socket::recv\n";
            return 0;
        }
        else if ( status == 0 )
            return 0;
        memcpy(&pszBuf[nPos], buf, status);
        nPos += status;
        if (buf[status - 1] == 0)
            break;
    }
    return nPos;
}

bool Socket::connect ( const std::string host, const int port )
{
    if ( ! is_valid() ) return false;

    m_addr.sin_family = AF_INET;
    m_addr.sin_port = htons ( port );

    inet_pton( AF_INET, host.c_str(), &m_addr.sin_addr );

    if ( errno == EAFNOSUPPORT ) return false;

    int status = ::connect( m_sock, (sockaddr *) &m_addr, sizeof(m_addr) );

    if ( status == 0 )
        return true;
    else
        return false;
}

void Socket::set_non_blocking ( const bool b )
{
    int opts = fcntl ( m_sock,
                       F_GETFL );

    if ( opts < 0 )
    {
        return;
    }

    if ( b )
        opts = ( opts | O_NONBLOCK );
    else
        opts = ( opts & ~O_NONBLOCK );

    fcntl ( m_sock,
            F_SETFL,opts );
}
